/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-02-08     Zhangyihong  the first version
 * 2018-04-03     XY           gt9xx for 1024 * 600
 * 2018-04-14     liu2guang    optimize int and rst to pin framework
 * 2017-08-08     XY           imxrt1052
 * 2018-10-29     XY
 */

#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"
#include "drv_gpio.h"
#include "drv_touch.h"
#include "drv_i2c.h"

#ifdef RT_USING_TOUCH

#define TP_INT_PIN GPIO_PORT_E,GPIO_PIN_2
#define TP_RST_PIN GPIO_PORT_E,GPIO_PIN_3

#ifndef TP_INT_PIN
#error "Please config touch panel INT pin."
#endif
#ifndef TP_RST_PIN
#error "Please config touch panel RST pin."
#endif


#ifndef IIC_RETRY_NUM
#define IIC_RETRY_NUM 2
#endif

#define MAX_TOUCH_POINT 5
#define TOUCH_POINT_BUFFER_SIZE 8

#define GT9xx_TS_ADDR               (0x14)
/*****/
#define GT9XX_SOFT_CFG      0x4080  // 软复位寄存器  先写0 后写 2 实现软复位  暂时用不上
#define GT9XX_VER_CFG       0x4780  // 外设配置版本寄存器
#define GT9XX_PID_CFG       0x4081  // 外设产品ID和版本寄存器
#define GT9XX_STATE_REG     0x4E81  // 外设触摸状态寄存器 最高位为1 表示数据准备OK &0x80 做判断 做完后记得置0
#define POINT_START_REG     0x4E81  // 这里因为写法上的原因将后续的8个寄存器全读取了 0x814E 作为判断 0x8150 0x8151 0x8152 0x8153 作为xy 中断方式是可以这样做做的
// #define POINT1_START_REG    0x5081
#define POINT1_START_REG    0x5781  // GT9147寄存器位置改变

#define GT9XX_CONF_LEN      186     // 外设的需要配置的寄存器长度
/*****/
#define TOUCH_I2C_NAME  "i2c2"

#if 1
#define TPDEBUG     rt_kprintf
#define LOG         rt_kprintf
#else
#define TPDEBUG(...)
#define LOG(...)
#endif

// 存一下读取的产品id
static char g_gt9xxx_pid[4] = {0};//GT911 or GT9147
static rt_dev_gt_touch_attr_t *g_touch_attr_t;
static struct rt_i2c_bus_device *i2c_bus;
static int gt9xxx_read(struct rt_i2c_bus_device * dev, rt_uint16_t reg, rt_uint8_t * buf, int len){
    struct rt_i2c_msg msgs[2];
    int ret;

    msgs[0].addr = dev->addr;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].len = 2;
    msgs[0].buf = &reg;

    msgs[1].addr = dev->addr;
    msgs[1].flags = RT_I2C_RD;
    msgs[1].len = len;
    msgs[1].buf = buf;
    //此处直接调用drv_i2c中的方法，请保证没有线程冲突
    //原始i2c_transfer(dev->i2c, msgs, 2) 如果在中断中调用，会因为锁问题停止
    ret=ingenic_i2c_xfer(dev, msgs, 2);//this must 2
    return ret;// < 0 ? ret : (ret != 2 ? -EIO : 0);
}

static rt_bool_t gt9xxx_write(struct rt_i2c_bus_device * dev, rt_uint16_t reg, rt_uint8_t * buf, int len){
    struct rt_i2c_msg msg;
    rt_uint8_t mbuf[256];

    if(len > sizeof(mbuf) - 1)
        len = sizeof(mbuf) - 1;
    mbuf[0] = reg & 0xff;
    mbuf[1] = (reg >> 8) & 0xff;
    rt_memcpy(&mbuf[2], buf, len);

    msg.addr = dev->addr;
    msg.flags = 0;
    msg.len = len + 2;
    msg.buf = &mbuf[0];
    //此处直接调用drv_i2c中的方法，请保证没有线程冲突
    //原始i2c_transfer(dev->i2c, &msg, 1) 如果在中断中调用，会因为锁问题停止
    if(ingenic_i2c_xfer(dev, &msg, 1) != 1)
        return RT_FALSE;
    return RT_TRUE;
}

// 911/9147 硬件复位
void gt9xx_hw_reset(rt_uint8_t address){
        gpio_set_func(TP_RST_PIN,IO_OUTPUT);
        gpio_direction_output(TP_RST_PIN,0);
        rt_thread_mdelay(20);//T2>=10ms
        //HIGH: 0x14,
        gpio_set_func(TP_INT_PIN,IO_OUTPUT);
        gpio_direction_output(TP_INT_PIN,1);

        rt_thread_mdelay(1);//T3>=100us

        gpio_direction_output(TP_RST_PIN,1);
        rt_thread_mdelay(8);//T4>=5ms
        //rt_pin_write(TP_INT_PIN, PIN_LOW);
        gpio_direction_output(TP_INT_PIN,0);
        rt_thread_mdelay(50);//50ms将INT悬浮输入
        gpio_set_pull_mode(TP_INT_PIN,PULL_DISABLE);
        gpio_direction_input(TP_INT_PIN);
}

static int gt9xx_send_cmd(struct rt_i2c_bus_device  * dev, rt_dev_gt_touch_attr_t *attr, rt_uint8_t cmd){
    rt_uint8_t buf[1]={cmd};
    rt_uint8_t ret;
    ret = gt9xxx_write(dev, attr->reg_ctrl, buf, 1);
    if(ret){
        LOG("soft reset gt9147  ok\n");
        return 0;
    }else{
        LOG("soft reset gt9147 failed\n");
        return 1;
    }
}

static int goodix_i2c_test(struct rt_i2c_bus_device * dev, rt_dev_gt_touch_attr_t *attr){
    int ret;
    int retry=0;
    rt_uint8_t test;
    while (retry++ < 2){
        ret = gt9xxx_read(dev, attr->reg_cfg, &test, 1);
        if(ret == 2){
            LOG("i2c test CFG_VER:%c\n",test);
            return 1;
        }
        LOG("i2c test failed attempt %d: %d\n",retry,test);
        rt_thread_mdelay(20);
    }
    return 0;
}

static int goodix_read_version(struct rt_i2c_bus_device * dev, rt_dev_gt_touch_attr_t *attr){
    int error;
    rt_uint8_t buf[6];
    error = gt9xxx_read(dev, attr->reg_product_id, buf, sizeof(buf));
    if (error !=2) {
        LOG("read id fail! error:%d \n",error);
        return error;
    }
    LOG("GT911 verson: %c%c%c%c_%02x%02x\n",buf[0],buf[1],buf[2],buf[3],buf[5],buf[4]);
    rt_memcpy(g_gt9xxx_pid, buf, sizeof(g_gt9xxx_pid));
    return 0;
}

static int goodix_send_cfg(struct rt_i2c_bus_device  * dev, rt_dev_gt_touch_attr_t *attr){
    int i;
    rt_uint8_t check_sum = 0;
    rt_uint8_t ret;
    rt_uint8_t *config = attr->cfg_table;
    rt_uint32_t len = attr->cfg_size;

    for (i = 0; i < (len-2); i++)
    {
        check_sum += config[i];
    }
    check_sum = (~check_sum) + 1;
    if (check_sum != config[len-2]) 
    { // 校验位
        LOG("check_sum err:%02x\n",check_sum);
        //return 0;
        config[len-2] = check_sum;
    }
    // 动态 赋予尺寸
    if(len == 186)
    {
        config[1] = LV_HOR_RES & 0xff;   // 低8位;
        config[2] = (LV_HOR_RES >> 8) & 0xff; // 高8位;
        config[3] = LV_VER_RES & 0xff;;
        config[4] = (LV_VER_RES >> 8) & 0xff;;
    }

    if (config[len - 1] != 0) 
    {// 00 不保存配置
        LOG("cfg err:last byte must be 0x00");
        config[len - 1] = 0;
        return 0;
    }

    ret = gt9xxx_write(dev, attr->reg_cfg, config, len);
    if(ret){
        LOG("send cfg ok\n");
        return 0;
    }else{
        LOG("send cfg err\n");
        return 1;
    }
}

static void gt9xx_interrupt(void * data){
    struct rt_i2c_bus_device  *dev = i2c_bus;
    rt_dev_gt_touch_attr_t *attr = g_touch_attr_t;
    struct touch_message msg;
    on_touch_message_handle on_touch_callback=(on_touch_message_handle)data;
    //rt_uint8_t cmd[2];
    rt_uint8_t state = 0;
    rt_uint8_t buf[TOUCH_POINT_BUFFER_SIZE] = {0};
    static rt_uint8_t s_tp_down = 0;
    int ret;
    gpio_irq_disable(TP_INT_PIN);

    ret = gt9xxx_read(dev, attr->reg_status, &state, 1);
    if(ret !=2)
    {
        LOG("read state err:%d\n",ret);
        return;
    }

    if(state & 0x80 != 1)
    {
        gpio_irq_enable(TP_INT_PIN);
        LOG("Touch Point buffer not ready");
        return;
    }

    ret = gt9xxx_read(dev, attr->reg_tp[0], buf, TOUCH_POINT_BUFFER_SIZE - 1);
    if(ret !=2)
    {
        LOG("read Touch Point err:%d\n",ret);
        return;
    }
    msg.x = ((rt_uint16_t)buf[1] << 8) | buf[0];
    msg.y = ((rt_uint16_t)buf[3] << 8) | buf[2];
    if((state & 0x01) == 0)
    {
       if(s_tp_down)
       {
           s_tp_down = 0;
           msg.event = TOUCH_EVENT_UP;
       }else
       {
          msg.event = TOUCH_EVENT_NONE;
       }
    }
    else
    {
       if(s_tp_down)
       {
           msg.event = TOUCH_EVENT_MOVE;
       }else
       {
           msg.event = TOUCH_EVENT_DOWN;
           s_tp_down = 1;
       }
    }
    if(on_touch_callback!=RT_NULL)
    {
        on_touch_callback(&msg);
    }
    // LOG("Touch panel x=%d y=%d event=0x%02x",msg.x,msg.y,msg.event );
    rt_uint8_t temp = 0;
    ret = gt9xxx_write(dev, GT9XX_STATE_REG, &temp, 1);
    if(!ret)
    {
        LOG("clear status err:%d",ret);
    }
    gpio_irq_enable(TP_INT_PIN);
}

static rt_bool_t gt9xx_probe(){
    //rt_uint8_t cmd[2];
    //rt_uint8_t buffer[6] = {0};
    rt_dev_gt_touch_attr_t *attr;
    i2c_bus = rt_i2c_bus_device_find(TOUCH_I2C_NAME);
    RT_ASSERT(i2c_bus);
    if(rt_device_open(&i2c_bus->parent, RT_DEVICE_OFLAG_RDWR) != RT_EOK)
    {
        TPDEBUG("[TP] %s I2C not open error!\n", TOUCH_I2C_NAME);
        return RT_FALSE;
    }
    attr = rt_get_tp_attr();
    g_touch_attr_t = attr;
    i2c_bus->addr=attr->i2c_addr;
    gt9xx_hw_reset(attr->i2c_addr);
    //gt9xx_soft_reset(i2c_bus);
    rt_thread_delay(RT_TICK_PER_SECOND / 5);
    if (!goodix_i2c_test(i2c_bus, attr))
    {
        TPDEBUG("[TP] %s goodix_i2c_test!  error\n", __func__);
        return RT_FALSE;
    }

    if(goodix_read_version(i2c_bus, attr))
    {
        TPDEBUG("[TP] %s goodix_read_version error!\n", __func__);
        return RT_FALSE;
    }
    rt_thread_mdelay(50);//maybe need 50ms before send config

    gt9xx_send_cmd(i2c_bus, attr, 0x02); // soft reset
    rt_thread_mdelay(10);
    if(goodix_send_cfg(i2c_bus, attr)){

    }
    rt_thread_mdelay(10);
    gt9xx_send_cmd(i2c_bus, attr, 0x00); // 读坐标

    gpio_irq_enable(TP_INT_PIN);
    return RT_FALSE;
}
void start_touch_listen(on_touch_message_handle call_back){
    gpio_set_func(TP_INT_PIN,IO_FUN_5);
    gpio_set_irq_type(TP_INT_PIN,NEGATIVE);
    gpio_set_irq_callback(TP_INT_PIN,gt9xx_interrupt,call_back);
    gpio_irq_enable(TP_INT_PIN);
}

static int gt9xx_driver_register(void){
    gt9xx_probe();
    return RT_EOK;
}
INIT_DEVICE_EXPORT(gt9xx_driver_register);

#endif
